#ifndef __MATHTOOLS_PLANE_H_INCLUDE__
#define __MATHTOOLS_PLANE_H_INCLUDE__
#include "mathToolsConfig.h"
#include "quaternion.h"
#include "vec3f.h"


namespace mt
{
	enum EIntersectionRelation3D
	{
		ISREL3D_FRONT = 0,
		ISREL3D_BACK,
		ISREL3D_PLANAR,
		ISREL3D_SPANNING,
		ISREL3D_CLIPPED
	};
	class mat4;
	class plane
	{
	public:
		plane();
		plane(const plane& other);
		plane(float _x, float _y, float _z, float _w);
		plane(const vec3f& normal, float d);
		plane(const vec3f& point1, const vec3f& point2, const vec3f& point3);
		plane(const vec3f& point, const vec3f& normal);
		explicit plane(const DirectX::XMFLOAT4& v);
		explicit plane(const float *pArray);
		plane(DirectX::XMVECTOR& V);

		DirectX::XMVECTOR getXMVector() const;

		void store(DirectX::XMVECTOR& v);
		// Comparision operators
		bool operator == (const plane& p) const;
		bool operator != (const plane& p) const;

		// Assignment operators
		plane& operator= (const plane& p);

		// Properties
		vec3f normal() const;
		void normal(const vec3f& normal);

		float d() const;
		void d(float d);

		// plane operations
		void Normalize();
		void Normalize(plane& result) const;

		float Dot(const DirectX::XMFLOAT4& v) const;

		float DotCoordinate(const vec3f& position) const;

		float DotNormal(const vec3f& normal) const;

		vec3f getMemberPoint()const;

		void setPlane(const vec3f& point, const vec3f& nvector);

		//! Recalculates the distance from origin by applying a new member point to the plane.
		void recalculateD(const vec3f& MPoint);

		//! Get the intersection point with two other planes if there is one.
		bool getIntersectionWithPlanes(const plane& o1,
			const plane& o2, vec3f& outPoint) const;

		//! Intersects this plane with another.
		/** \param other Other plane to intersect with.
		\param outLinePoint Base point of intersection line.
		\param outLineVect Vector of intersection.
		\return True if there is a intersection, false if not. */
		bool getIntersectionWithPlane(const plane& other,
			vec3f& outLinePoint,
			vec3f& outLineVect) const;

		//! Get an intersection with a 3d line.
		/** \param lineVect Vector of the line to intersect with.
		\param linePoint Point of the line to intersect with.
		\param outIntersection Place to store the intersection point, if there is one.
		\return True if there was an intersection, false if there was not.
		*/
		bool getIntersectionWithLine(const vec3f& linePoint,
			const vec3f& lineVect,
			vec3f& outIntersection) const;

		//! Classifies the relation of a point to this plane.
		/** \param point Point to classify its relation.
		\return ISREL3D_FRONT if the point is in front of the plane,
		ISREL3D_BACK if the point is behind of the plane, and
		ISREL3D_PLANAR if the point is within the plane. */
		EIntersectionRelation3D classifyPointRelation(const vec3f& point) const;

		//! Get percentage of line between two points where an intersection with this plane happens.
		/** Only useful if known that there is an intersection.
		\param linePoint1 Point1 of the line to intersect with.
		\param linePoint2 Point2 of the line to intersect with.
		\return Where on a line between two points an intersection with this plane happened.
		For example, 0.5 is returned if the intersection happened exactly in the middle of the two points.
		*/
		float getKnownIntersectionWithLine(const vec3f& linePoint1,
			const vec3f& linePoint2) const;

		// Static functions
		static void Transform(plane& result, const plane& plane0, const DirectX::XMFLOAT4X4& M);

		static void Transform(plane& result, const plane& plane0, const mat4& m);

		static plane Transform(const plane& pl, const DirectX::XMFLOAT4X4& M);

		static void Transform(const plane& plane0, const quaternion& rotation, plane& result);

		static plane Transform(const plane& plane0, const quaternion& rotation);

		union
		{
			float M[4];
			struct 
			{
				vec3f Normal;
				float D;
			}SND;
			DirectX::XMFLOAT4 Data;
		};
	};
}

#endif//! __MATHTOOLS_PLANE_H_INCLUDE__